package com.evildog.workspace.system.application.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.evildog.workspace.common.object.PageQuery;
import com.evildog.workspace.common.result.PagedResult;
import com.evildog.workspace.dependencies.mybatis.base.TableLinkService;
import com.evildog.workspace.dependencies.mybatis.enums.BaseStatusEnum;
import com.evildog.workspace.dependencies.mybatis.object.BaseStatusDTO;
import com.evildog.workspace.dependencies.mybatis.object.QueryConditionDTO;
import com.evildog.workspace.dependencies.mybatis.util.BaseStatusValidate;
import com.evildog.workspace.dependencies.mybatis.util.QueryConditionUtils;
import com.evildog.workspace.system.application.bo.*;
import com.evildog.workspace.system.application.convert.ApiPermissionConvert;
import com.evildog.workspace.system.application.dto.ApiPermissionDTO;
import com.evildog.workspace.system.application.dto.ApiPermissionPageDTO;
import com.evildog.workspace.system.application.entity.*;
import com.evildog.workspace.system.application.mapper.ApiPermissionMapper;
import com.evildog.workspace.system.application.mapper.ComponentApiPermissionLinkMapper;
import com.evildog.workspace.system.application.mapper.MenuApiPermissionLinkMapper;
import com.evildog.workspace.system.application.service.ApiPermissionCategoryService;
import com.evildog.workspace.system.application.service.ApiPermissionService;
import com.evildog.workspace.system.application.service.RoleService;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.Multimaps;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.*;
import java.util.stream.Collectors;

@Service
@AllArgsConstructor
public class ApiPermissionServiceImpl extends ServiceImpl<ApiPermissionMapper, ApiPermission> implements ApiPermissionService {

    private final RoleService roleService;

    private final ApiPermissionCategoryService apiPermissionCategoryService;

    private final MenuApiPermissionLinkMapper menuApiPermissionLinkMapper;
    private final ComponentApiPermissionLinkMapper componentApiPermissionLinkMapper;

    private final TableLinkService tableLinkService;

    @Override
    public PagedResult<ApiPermissionPageBO> getPageList(PageQuery query, List<QueryConditionDTO> queryConditions, ApiPermissionPageDTO pageDTO) {
        IPage<ApiPermissionPageDO> page = this.baseMapper.getPageList(
                QueryConditionUtils.getPagination(query),
                queryConditions,
                pageDTO);
        return ApiPermissionConvert.INSTANCE.convert(page);
    }

    @Override
    public ApiPermissionBO getDetailById(Long id) {
        ApiPermission apiPermission = this.getById(id);
        Assert.notNull(apiPermission, "apiPermission is null!");
        return ApiPermissionConvert.INSTANCE.convert(apiPermission);
    }

    @Override
    public boolean save(ApiPermissionDTO dto) {
        ApiPermission newApiPermission = ApiPermissionConvert.INSTANCE.convert(dto);
        return save(newApiPermission);
    }

    @Override
    public boolean updateById(Long id, ApiPermissionDTO dto) {
        ApiPermission oldApiPermission = getById(id);
        Assert.notNull(oldApiPermission, "apiPermission is null!");
        ApiPermission newApiPermission = ApiPermissionConvert.INSTANCE.convert(dto);
        newApiPermission.setId(id);
        return updateById(newApiPermission);
    }

    @Override
    public List<ApiPermissionClientBO> getAllPermissions() {
        List<ApiPermission> list = list(
                Wrappers.<ApiPermission>lambdaQuery()
                        .select(ApiPermission::getPath, ApiPermission::getMethod)
                        .eq(ApiPermission::getStatus, BaseStatusEnum.IS_ABLE.getStatus())
        );

        return ApiPermissionConvert.INSTANCE.convertClientBO(list);
    }

    @Override
    public List<ApiPermissionClientBO> getUserPermissions(Long userId) {
        List<Long> menuPermissionIds = getMenuPermissionIds(userId);
        List<Long> componentPermissionIds = getComponentIds(userId);

        List<Long> menuApiList = tableLinkService.getLinkIdList(
                menuApiPermissionLinkMapper,
                MenuApiPermissionLink::getApiPermissionId,
                MenuApiPermissionLink::getMenuPermissionId, menuPermissionIds
        );

        List<Long> componentApiList = tableLinkService.getLinkIdList(
                componentApiPermissionLinkMapper,
                ComponentApiPermissionLink::getApiPermissionId,
                ComponentApiPermissionLink::getComponentPermissionId, componentPermissionIds
        );


        Set<Long> allUiPermissionIds = new HashSet<>();
        allUiPermissionIds.addAll(menuApiList);
        allUiPermissionIds.addAll(componentApiList);

        List<ApiPermission> list = list(
                Wrappers.<ApiPermission>lambdaQuery()
                        .select(ApiPermission::getPath, ApiPermission::getMethod)
                        .eq(ApiPermission::getStatus, BaseStatusEnum.IS_ABLE.getStatus())
                        .in(ApiPermission::getId, allUiPermissionIds)
        );

        // TODO 加入redis缓存

        return ApiPermissionConvert.INSTANCE.convertClientBO(list);
    }

    private List<Long> getComponentIds(Long userId) {
        List<ComponentPermission> componentPermissions = roleService.getComponentPermissions(userId);
        return componentPermissions.stream().map(ComponentPermission::getId).collect(Collectors.toList());
    }

    private List<Long> getMenuPermissionIds(Long userId) {
        List<MenuPermission> menuPermissions = roleService.getMenuPermissions(userId);
        return menuPermissions.stream().map(MenuPermission::getId).collect(Collectors.toList());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean remove(Long id) {
        if (removeById(id)) {
            // 删除与组件之间的关联
            tableLinkService.removeByLinkId(componentApiPermissionLinkMapper, ComponentApiPermissionLink::getApiPermissionId, id);

            // 删除与菜单之间的关联
            tableLinkService.removeByLinkId(menuApiPermissionLinkMapper, MenuApiPermissionLink::getApiPermissionId, id);

            return true;
        } else {
            return false;
        }
    }

    @Override
    public List<ApiPermissionAllocBO> getAllocList() {
        List<ApiPermissionCategory> apiPermissionCategories = apiPermissionCategoryService.list();
        Map<Long, ApiPermissionCategory> apiPermissionCategoryMap = apiPermissionCategories.stream()
                .collect(Collectors.toMap(ApiPermissionCategory::getId, cate -> cate));

        List<ApiPermission> apiPermissions = list(Wrappers.<ApiPermission>lambdaQuery()
                .eq(ApiPermission::getStatus, BaseStatusEnum.IS_ABLE.getStatus())
        );


        // key categoryId value List<ApiPermission>
        ImmutableListMultimap<Long, ApiPermission> apiPermissionMap = Multimaps.index(apiPermissions, ApiPermission::getCategoryId);

        List<ApiPermissionAllocBO> apiPermissionAllocBOList = new ArrayList<>();

        apiPermissionMap.asMap().forEach((categoryId, apiPermissionList) -> {
            List<ApiPermissionBO> apiPermissionBoList = new ArrayList<>();

            apiPermissionList.forEach(api -> {
                apiPermissionBoList.add(ApiPermissionConvert.INSTANCE.convert(api));
            });


            apiPermissionAllocBOList.add(
                    new ApiPermissionAllocBO()
                            .setCategoryId(categoryId)
                            .setCategoryName(apiPermissionCategoryMap.get(categoryId).getName())
                            .setApiPermissionList(apiPermissionBoList)
            );
        });


        return apiPermissionAllocBOList;
    }

    @Override
    public List<ApiPermissionListBO> getListByMenu(Long menuPermissionId) {
        List<Long> apiIds = tableLinkService.getLinkIdList(menuApiPermissionLinkMapper, MenuApiPermissionLink::getApiPermissionId, MenuApiPermissionLink::getMenuPermissionId, menuPermissionId);

        if(apiIds.isEmpty()){
            return new ArrayList<>();
        }

        List<ApiPermission> list = list(Wrappers.<ApiPermission>lambdaQuery()
                .in(ApiPermission::getId, apiIds)
                .eq(ApiPermission::getStatus, BaseStatusEnum.IS_ABLE.getStatus())
        );

        return ApiPermissionConvert.INSTANCE.convertListBo(list);
    }

    @Override
    public List<ApiPermissionListBO> getListByComponentId(Long componentPermissionId) {
        List<Long> apiIds = tableLinkService.getLinkIdList(componentApiPermissionLinkMapper, ComponentApiPermissionLink::getApiPermissionId, ComponentApiPermissionLink::getComponentPermissionId, componentPermissionId);

        if(apiIds.isEmpty()){
            return new ArrayList<>();
        }

        List<ApiPermission> list = list(Wrappers.<ApiPermission>lambdaQuery()
                .in(ApiPermission::getId, apiIds)
                .eq(ApiPermission::getStatus, BaseStatusEnum.IS_ABLE.getStatus())
        );

        return ApiPermissionConvert.INSTANCE.convertListBo(list);
    }

    @Override
    public boolean updateStatus(Long id, BaseStatusDTO baseStatus) {
        ApiPermission apiPermission = getById(id);
        Assert.notNull(apiPermission, "apiPermission is null");

        Integer status = baseStatus.getStatus();
        BaseStatusValidate.checkStatusValid(status);

        ApiPermission updateDO = new ApiPermission()
                .setId(id)
                .setStatus(status);
        return updateById(updateDO);
    }

}
